#include "ping_interface_common.h"
#include <ctype.h>
#include <sched.h>
#include <math.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <ifaddrs.h>


static int screen_width = INT_MAX;
static int confirm_flag = MSG_CONFIRM;
static volatile int  exiting;
static int timing = 1;			/* flag to do timing */
static int confirm = 0;


int modify_capability(int on, uid_t euid)
{
	if (seteuid(on ? euid : getuid())) {
		perror("seteuid");
		return -1;
	}

	return 0;
}

static long llsqrt(long long a)
{
	long long prev = ~((long long)1 << 63);
	long long x = a;

	if (x > 0) {
		while (x < prev) {
			prev = x;
			x = (x+(a/x))/2;
		}
	}

	return (long)x;
}

static inline int in_flight(st_ping_status* pstatus)
{
	__u16 diff = (__u16)(pstatus->ntransmitted) - pstatus->acked;
	return (diff<=0x7FFF) ? diff : pstatus->ntransmitted-pstatus->nreceived-pstatus->nerrors;
}


static inline int schedule_exit(int next, ping_configuration_packet* pst_ping_conf)
{
	struct itimerval it;
	st_ping_status* pstatus =  &pst_ping_conf->ping_status;
	if (pst_ping_conf->npackets  && !pstatus->deadline 
					&& pstatus->ntransmitted >= pst_ping_conf->npackets) {

		if (pstatus->waittime) {
			return next;
		}
		
		if (pstatus->nreceived) {
			pstatus->waittime = 2 * pstatus->tmax;
			if (pstatus->waittime < 1000*pst_ping_conf->interval)
				pstatus->waittime = 1000*pst_ping_conf->interval;
		} else {
			pstatus->waittime = pst_ping_conf->lingertime*1000;
		}

		if (next < 0 || next < pstatus->waittime/1000) {
			next = pstatus->waittime/1000;
		}
		
		it.it_interval.tv_sec = 0;
		it.it_interval.tv_usec = 0;
		it.it_value.tv_sec = pstatus->waittime/1000000;
		it.it_value.tv_usec = pstatus->waittime%1000000;
		setitimer(ITIMER_REAL, &it, NULL);
	}
	return next;
}


/*
 * pr_addr --
 *	Return an ascii host address as a dotted quad and optionally with  schedule_exit
 * a hostname.  pr_options
 */
char * pr_addr(__u32 addr, ping_configuration_packet* pst_ping_conf)
{
	struct hostent *hp;
	static char buf[4096];
	st_ping_status* pstatus =  &pst_ping_conf->ping_status;

	pstatus->in_pr_addr = !setjmp(pstatus->pr_addr_jmp);

	if (pstatus->exiting || (pst_ping_conf->options & F_NUMERIC) ||
	    !(hp = gethostbyaddr((char *)&addr, 4, AF_INET))) {
		sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&addr));
	}

	else {
		char *s;
#if USE_IDN
		if (idna_to_unicode_lzlz(hp->h_name, &s, 0) != IDNA_SUCCESS)
			s = NULL;
#else
		s = NULL;
#endif
		snprintf(buf, sizeof(buf), "%s (%s)", s ? s : hp->h_name,
			 inet_ntoa(*(struct in_addr *)&addr));
#if USE_IDN
		free(s);
#endif
	}

	pstatus->in_pr_addr = 0;

	return(buf);
}


/*
 * Write to stdout
 */
static inline void write_stdout(const char *str, size_t len)
{
	size_t o = 0;
	ssize_t cc;
	do {
		cc = write(STDOUT_FILENO, str + o, len - o);
		o += cc;
	} while (len > o || cc < 0);
}

static inline void acknowledge(__u16 seq, st_ping_status* pstatus)
{
	__u16 diff = (__u16)pstatus->ntransmitted - seq;
	if (diff <= 0x7FFF) {
		if ((int)diff+1 > pstatus->pipesize)
			pstatus->pipesize = (int)diff+1;
		if ((__s16)(seq - pstatus->acked) > 0 ||
		    (__u16)pstatus->ntransmitted - pstatus->acked > 0x7FFF)
			pstatus->acked = seq;
	}
}


static inline void advance_ntransmitted(st_ping_status* pstatus)
{
	pstatus->ntransmitted++;
	/* Invalidate acked, if 16 bit seq overflows. */
	if ((__u16)(pstatus->ntransmitted) - pstatus->acked > 0x7FFF)
		pstatus->acked = (__u16)(pstatus->ntransmitted) + 1;
}

static inline int is_ours(uint16_t id) {
	//return id == 0;
	return 1;
}

static inline void tvsub(struct timeval *out, struct timeval *in)
{
	if ((out->tv_usec -= in->tv_usec) < 0) {
		--out->tv_sec;
		out->tv_usec += 1000000;
	}
	out->tv_sec -= in->tv_sec;
}

static u_short in_cksum(const u_short *addr, register int len, u_short csum)
{
	register int nleft = len;
	const u_short *w = addr;
	register u_short answer;
	register int sum = csum;

	/*
	 *  Our algorithm is simple, using a 32 bit accumulator (sum),
	 *  we add sequential 16 bit words to it, and at the end, fold
	 *  back all the carry bits from the top 16 bits into the lower
	 *  16 bits.
	 */
	while (nleft > 1)  {
		sum += *w++;
		nleft -= 2;
	}

	/* mop up an odd byte, if necessary */
	if (nleft == 1)
		sum += ODDBYTE(*(u_char *)w); /* le16toh() may be unavailable on old systems */

	/*
	 * add back carry outs from top 16 bits to low 16 bits
	 */
	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
	sum += (sum >> 16);			/* add carry */
	answer = ~sum;				/* truncate to 16 bits */
	return (answer);
}


static inline void set_signal(int signo, void (*handler)(int))
{
	struct sigaction sa;

	memset(&sa, 0, sizeof(sa));

	sa.sa_handler = (void (*)(int))handler;
#ifdef SA_INTERRUPT
	sa.sa_flags = SA_INTERRUPT;
#endif
	sigaction(signo, &sa, NULL);
}

static void sigexit(int signo)
{
	exiting = 1;          //ntransmitted
	//fprintf(stderr, "in_pr_addr = %d.\n",in_pr_addr );
	//if (in_pr_addr)
	//	longjmp(pr_addr_jmp, 0);
}

static void sock_setmark(int icmp_sock) {
	/* todo options*/
}

/*
 * Print timestamp
 */
static void print_timestamp(int options)
{
	if (options & F_PTIMEOFDAY) {
		struct timeval tv;
		gettimeofday(&tv, NULL);
		printf("[%lu.%06lu] ",
		       (unsigned long)tv.tv_sec, (unsigned long)tv.tv_usec);
	}
}

static inline void update_interval(ping_configuration_packet* pst_ping_conf)
{
	st_ping_status* pstatus =  &pst_ping_conf->ping_status;
	int est = pstatus->rtt ? pstatus->rtt/8 : pst_ping_conf->interval*1000;

	pst_ping_conf->interval = (est + pst_ping_conf->rtt_addend + 500)/1000;
	if (pst_ping_conf->uid && pst_ping_conf->interval < MINUSERINTERVAL)
		pst_ping_conf->interval = MINUSERINTERVAL;
}

void pr_options(unsigned char * cp, int hlen, ping_configuration_packet* pst_ping_conf, char* out_buf)
{
	int i, j;
	int optlen, totlen;
	unsigned char * optptr;
	static int old_rrlen;
	static char old_rr[MAX_IPOPTLEN];

	totlen = hlen-sizeof(struct iphdr);
	optptr = cp;
	int options = pst_ping_conf->options;

	while (totlen > 0) {
		if (*optptr == IPOPT_EOL)
			break;
		if (*optptr == IPOPT_NOP) {
			totlen--;
			optptr++;
			sprintf(out_buf, "%s \nNOP", out_buf);
			continue;
		}
		cp = optptr;
		optlen = optptr[1];
		if (optlen < 2 || optlen > totlen)
			break;

		switch (*cp) {
		case IPOPT_SSRR:
		case IPOPT_LSRR:
			sprintf(out_buf,"%s \n%cSRR: ", out_buf, *cp==IPOPT_SSRR ? 'S' : 'L');
			j = *++cp;
			i = *++cp;
			i -= 4;
			cp++;
			if (j > IPOPT_MINOFF) {
				for (;;) {
					__u32 address;
					memcpy(&address, cp, 4);
					cp += 4;
					if (address == 0)
						sprintf(out_buf,"%s \t0.0.0.0", out_buf);
					else
						sprintf(out_buf,"%s \t%s", out_buf, pr_addr(address, pst_ping_conf));
					j -= 4;
					putchar('\n');
					if (j <= IPOPT_MINOFF)
						break;
				}
			}
			break;
		case IPOPT_RR:
			j = *++cp;		/* get length */
			i = *++cp;		/* and pointer */
			if (i > j)
				i = j;
			i -= IPOPT_MINOFF;
			if (i <= 0)
				break;
			if (i == old_rrlen
			    && !memcmp(cp, old_rr, i)
			    && !(options & F_FLOOD)) {  //F_VERBOSE
				sprintf(out_buf,"%s \t(same route)", out_buf);
				i = ((i + 3) / 4) * 4;
				cp += i;
				break;
			}
			old_rrlen = i;
			memcpy(old_rr, (char *)cp, i);
			sprintf(out_buf,"%s \nRR: ", out_buf);
			cp++;
			for (;;) {
				__u32 address;
				memcpy(&address, cp, 4);
				cp += 4;
				if (address == 0)
					sprintf(out_buf,"%s \t0.0.0.0", out_buf);
				else
					sprintf(out_buf,"%s \t%s", out_buf, pr_addr(address, pst_ping_conf));
				i -= 4;
				putchar('\n');
				if (i <= 0)
					break;
			}
			break;
		case IPOPT_TS:
		{
			int stdtime = 0, nonstdtime = 0;
			__u8 flags;
			j = *++cp;		/* get length */
			i = *++cp;		/* and pointer */
			if (i > j)
				i = j;
			i -= 5;
			if (i <= 0)
				break;
			flags = *++cp;
			sprintf(out_buf,"%s\nTS: ", out_buf);
			cp++;
			for (;;) {
				long l;

				if ((flags&0xF) != IPOPT_TS_TSONLY) {
					__u32 address;
					memcpy(&address, cp, 4);
					cp += 4;
					if (address == 0)
						sprintf(out_buf,"%s \t0.0.0.0", out_buf);
					else
						sprintf(out_buf,"%s \t%s", out_buf, pr_addr(address, pst_ping_conf));
					i -= 4;
					if (i <= 0)
						break;
				}
				l = *cp++;
				l = (l<<8) + *cp++;
				l = (l<<8) + *cp++;
				l = (l<<8) + *cp++;

				if  (l & 0x80000000) {
					if (nonstdtime==0)
						sprintf(out_buf,"%s \t%ld absolute not-standard", out_buf, l&0x7fffffff);
					else
						sprintf(out_buf,"%s \t%ld not-standard", out_buf, (l&0x7fffffff) - nonstdtime);
					nonstdtime = l&0x7fffffff;
				} else {
					if (stdtime==0)
						sprintf(out_buf,"%s \t%ld absolute", out_buf, l);
					else
						sprintf(out_buf,"%s \t%ld", out_buf, l - stdtime);
					stdtime = l;
				}
				i -= 4;
				putchar('\n');
				if (i <= 0)
					break;
			}
			if (flags>>4)
				sprintf(out_buf,"%s Unrecorded hops: %d\n", out_buf, flags>>4);
			break;
		}
		default:
			sprintf(out_buf,"%s \nunknown option %x", out_buf, *cp);
			break;
		}
		totlen -= optlen;
		optptr += optlen;
	}
}


/*
 * pr_iph --
 *	Print an IP header with options.
 */
void pr_iph(struct iphdr *ip, ping_configuration_packet* pst_ping_conf, char* out_buf)
{
	int hlen;
	u_char *cp;

	hlen = ip->ihl << 2;
	cp = (u_char *)ip + 20;		/* point to options */

	sprintf(out_buf, "%s Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst Data\n", out_buf);
	sprintf(out_buf, "%s %1x  %1x  %02x %04x %04x",
	       out_buf, ip->version, ip->ihl, ip->tos, ip->tot_len, ip->id);
	sprintf(out_buf, "%s   %1x %04x", out_buf, ((ip->frag_off) & 0xe000) >> 13,
	       (ip->frag_off) & 0x1fff);
	sprintf(out_buf, "%s  %02x  %02x %04x", out_buf, ip->ttl, ip->protocol, ip->check);
	sprintf(out_buf, "%s %s ", out_buf, inet_ntoa(*(struct in_addr *)&ip->saddr));
	sprintf(out_buf, "%s %s ", out_buf, inet_ntoa(*(struct in_addr *)&ip->daddr));
	sprintf(out_buf, "%s\n", out_buf);
	pr_options(cp, hlen, pst_ping_conf, out_buf);
}


/*
 * pr_icmph --
 *	Print a descriptive string about an ICMP header.
 */
void pr_icmph(__u8 type, __u8 code, __u32 info, struct icmphdr *icp, 
					ping_configuration_packet* pst_ping_conf, char* out_buf)
{
	int options = pst_ping_conf->options;
	switch(type) {
	case ICMP_ECHOREPLY:
		sprintf(out_buf,"Echo Reply\n");
		/* XXX ID + Seq + Data */
		break;
	case ICMP_DEST_UNREACH:
		switch(code) {
		case ICMP_NET_UNREACH:
			sprintf(out_buf,"Destination Net Unreachable\n");
			break;
		case ICMP_HOST_UNREACH:
			sprintf(out_buf,"Destination Host Unreachable\n");
			break;
		case ICMP_PROT_UNREACH:
			sprintf(out_buf,"Destination Protocol Unreachable\n");
			break;
		case ICMP_PORT_UNREACH:
			sprintf(out_buf,"Destination Port Unreachable\n");
			break;
		case ICMP_FRAG_NEEDED:
			sprintf(out_buf,"Frag needed and DF set (mtu = %u)\n", info);
			break;
		case ICMP_SR_FAILED:
			sprintf(out_buf,"Source Route Failed\n");
			break;
		case ICMP_NET_UNKNOWN:
			sprintf(out_buf,"Destination Net Unknown\n");
			break;
		case ICMP_HOST_UNKNOWN:
			sprintf(out_buf,"Destination Host Unknown\n");
			break;
		case ICMP_HOST_ISOLATED:
			sprintf(out_buf,"Source Host Isolated\n");
			break;
		case ICMP_NET_ANO:
			sprintf(out_buf,"Destination Net Prohibited\n");
			break;
		case ICMP_HOST_ANO:
			sprintf(out_buf,"Destination Host Prohibited\n");
			break;
		case ICMP_NET_UNR_TOS:
			sprintf(out_buf,"Destination Net Unreachable for Type of Service\n");
			break;
		case ICMP_HOST_UNR_TOS:
			sprintf(out_buf,"Destination Host Unreachable for Type of Service\n");
			break;
		case ICMP_PKT_FILTERED:
			sprintf(out_buf,"Packet filtered\n");
			break;
		case ICMP_PREC_VIOLATION:
			sprintf(out_buf,"Precedence Violation\n");
			break;
		case ICMP_PREC_CUTOFF:
			sprintf(out_buf,"Precedence Cutoff\n");
			break;
		default:
			sprintf(out_buf,"Dest Unreachable, Bad Code: %d\n", code);
			break;
		}
		if (icp && (options & F_VERBOSE)) {
			pr_iph((struct iphdr*)(icp + 1), pst_ping_conf, out_buf);	
		}
			
		break;
	case ICMP_SOURCE_QUENCH:
		sprintf(out_buf,"Source Quench\n");
		if (icp && (options & F_VERBOSE)) {
			pr_iph((struct iphdr*)(icp + 1), pst_ping_conf, out_buf);
		}
			
		break;
	case ICMP_REDIRECT:
		switch(code) {
		case ICMP_REDIR_NET:
			sprintf(out_buf,"Redirect Network");
			break;
		case ICMP_REDIR_HOST:
			sprintf(out_buf,"Redirect Host");
			break;
		case ICMP_REDIR_NETTOS:
			sprintf(out_buf,"Redirect Type of Service and Network");
			break;
		case ICMP_REDIR_HOSTTOS:
			sprintf(out_buf,"Redirect Type of Service and Host");
			break;
		default:
			sprintf(out_buf,"Redirect, Bad Code: %d", code);
			break;
		}
		sprintf(out_buf,"(New nexthop: %s)\n", pr_addr(icp ? icp->un.gateway : info, pst_ping_conf));
		if (icp && (options & F_VERBOSE)) { //F_QUIET
			pr_iph((struct iphdr*)(icp + 1), pst_ping_conf, out_buf);
		}
			
		break;
	case ICMP_ECHO:
		sprintf(out_buf,"Echo Request\n");
		/* XXX ID + Seq + Data */
		break;
	case ICMP_TIME_EXCEEDED:
		switch(code) {
		case ICMP_EXC_TTL:
			sprintf(out_buf,"Time to live exceeded\n");
			break;
		case ICMP_EXC_FRAGTIME:
			sprintf(out_buf,"Frag reassembly time exceeded\n");
			break;
		default:
			sprintf(out_buf,"Time exceeded, Bad Code: %d\n", code);
			break;
		}
		if (icp && (options & F_VERBOSE))
			pr_iph((struct iphdr*)(icp + 1), pst_ping_conf, out_buf);
		break;
	case ICMP_PARAMETERPROB:
		sprintf(out_buf,"Parameter problem: pointer = %u\n", icp ? (ntohl(icp->un.gateway)>>24) : info);
		if (icp && (options & F_VERBOSE))
			pr_iph((struct iphdr*)(icp + 1), pst_ping_conf, out_buf);
		break;
	case ICMP_TIMESTAMP:
		sprintf(out_buf,"Timestamp\n");
		/* XXX ID + Seq + 3 timestamps */
		break;
	case ICMP_TIMESTAMPREPLY:
		sprintf(out_buf,"Timestamp Reply\n");
		/* XXX ID + Seq + 3 timestamps */
		break;
	case ICMP_INFO_REQUEST:
		sprintf(out_buf,"Information Request\n");
		/* XXX ID + Seq */
		break;
	case ICMP_INFO_REPLY:
		sprintf(out_buf,"Information Reply\n");
		/* XXX ID + Seq */
		break;
#ifdef ICMP_MASKREQ
	case ICMP_MASKREQ:
		sprintf(out_buf,"Address Mask Request\n");
		break;
#endif
#ifdef ICMP_MASKREPLY
	case ICMP_MASKREPLY:
		sprintf(out_buf,"Address Mask Reply\n");
		break;
#endif
	default:
		sprintf(out_buf,"Bad ICMP type: %d\n", type);
	}
}

void pr_echo_reply(__u8 *_icp, int len)
{
	struct icmphdr * icp = (struct icmphdr *)_icp;
	printf(" icmp_seq=%u", ntohs(icp->un.echo.sequence));
}



void sock_setbufs(ping_configuration_packet* pst_ping_config, int alloc)
{
	int rcvbuf, hold;
	socklen_t tmplen = sizeof(hold);

	if (!pst_ping_config->sndbuf)
		pst_ping_config->sndbuf = alloc;
	setsockopt(pst_ping_config->icmp_sock, SOL_SOCKET, SO_SNDBUF, 
						(char *)&pst_ping_config->sndbuf, sizeof(pst_ping_config->sndbuf));

	rcvbuf = hold = alloc * pst_ping_config->preload;
	if (hold < 65536)
		hold = 65536;
	setsockopt(pst_ping_config->icmp_sock, SOL_SOCKET, SO_RCVBUF, (char *)&hold, sizeof(hold));
	if (getsockopt(pst_ping_config->icmp_sock, SOL_SOCKET, SO_RCVBUF, (char *)&hold, &tmplen) == 0) {
		if (hold < rcvbuf)
			fprintf(stderr, "WARNING: probably, rcvbuf is not enough to hold preload.\n");
	}
}


static int receive_error_msg(ping_configuration_packet* pst_ping_conf)
{
	int res;
	char cbuf[512];
	struct iovec  iov;
	struct msghdr msg;
	struct cmsghdr *cmsg;
	struct sock_extended_err *e;
	struct icmphdr icmph;
	struct sockaddr_in target;
	int net_errors = 0;
	int local_errors = 0;
	int saved_errno = errno;
	char hearbuf[512];

	iov.iov_base = &icmph;
	iov.iov_len = sizeof(icmph);
	msg.msg_name = (void*)&target;
	msg.msg_namelen = sizeof(target);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_flags = 0;
	msg.msg_control = cbuf;
	msg.msg_controllen = sizeof(cbuf);

	res = recvmsg(pst_ping_conf->icmp_sock, &msg, MSG_ERRQUEUE|MSG_DONTWAIT);
	if (res < 0)
		goto out;

	e = NULL;
	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
		if (cmsg->cmsg_level == SOL_IP) {
			if (cmsg->cmsg_type == IP_RECVERR)
				e = (struct sock_extended_err *)CMSG_DATA(cmsg);
		}
	}
	if (e == NULL)
		abort();

	if (e->ee_origin == SO_EE_ORIGIN_LOCAL) {
		local_errors++;
		if (pst_ping_conf->options & F_QUIET) {
			goto out;
		}
			
		if (pst_ping_conf->options & F_FLOOD) {
			write_stdout("E", 1);
		}
			
		else if (e->ee_errno != EMSGSIZE)
			__log_error("ping: local error: %s\n", strerror(e->ee_errno));
		else
			__log_error("ping: local error: Message too long, mtu=%u\n", e->ee_info);
		pst_ping_conf->ping_status.nerrors++;
	} else if (e->ee_origin == SO_EE_ORIGIN_ICMP) {
		struct sockaddr_in *sin = (struct sockaddr_in*)(e+1);
		int error_pkt;

		if (res < sizeof(icmph) ||
		    target.sin_addr.s_addr != pst_ping_conf->destination.sin_addr.s_addr ||
		    icmph.type != ICMP_ECHO ||
		    !is_ours(icmph.un.echo.id)) {
			/* Not our error, not an error at all. Clear. */
			saved_errno = 0;
			goto out;
		}

		error_pkt = (e->ee_type != ICMP_REDIRECT &&
			     e->ee_type != ICMP_SOURCE_QUENCH);
		if (error_pkt) {
			acknowledge(ntohs(icmph.un.echo.sequence), &pst_ping_conf->ping_status);
			net_errors++;
			pst_ping_conf->ping_status.nerrors++;
		}
		else {
			saved_errno = 0;
		}

		/*
		if (!working_recverr) {
			struct icmp_filter filt;
			working_recverr = 1;

			filt.data = ~((1<<ICMP_SOURCE_QUENCH)|
				      (1<<ICMP_REDIRECT)|
				      (1<<ICMP_ECHOREPLY));
			if (setsockopt(icmp_sock, SOL_RAW, ICMP_FILTER, (char*)&filt, sizeof(filt)) == -1)
				perror("\rWARNING: setsockopt(ICMP_FILTER)");
		}
		*/

		if (pst_ping_conf->options & F_QUIET) {  
			goto out;
		}
			
		if (pst_ping_conf->options & F_FLOOD) {
			if (error_pkt)
				write_stdout("\bE", 2);
		} else {
			print_timestamp(pst_ping_conf->options);
			memset((void*)hearbuf, '0', sizeof(hearbuf));
			pr_icmph(e->ee_type, e->ee_code, e->ee_info, NULL, pst_ping_conf, hearbuf);
			printf("From %s: icmp_seq=%u ", 
					pr_addr(sin->sin_addr.s_addr, pst_ping_conf), ntohs(icmph.un.echo.sequence));
			__log_debug("%s", hearbuf);
			fflush(stdout);
		}
	}

out:
	errno = saved_errno;
	return net_errors ? : -local_errors;
}


/* Protocol independent setup and parameter checks. */
void setup(ping_configuration_packet* pst_ping_config)
{
	int hold;
	struct timeval tv;
	sigset_t sset;

	if (pst_ping_config->uid && pst_ping_config->interval < MINUSERINTERVAL) {
		__log_error("ping: cannot flood; minimal interval, allowed for user, is %dms\n", MINUSERINTERVAL);
		exit(2);
	}
	if (pst_ping_config->interval >= INT_MAX/pst_ping_config->preload) {
		__log_error("ping: illegal preload and/or interval\n");
		exit(2);
	}

	hold = 1;


	int on = 1;
	if (setsockopt(pst_ping_config->icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)))
		__log_debug("Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP\n");



	sock_setmark(pst_ping_config->icmp_sock);

	/* Set some SNDTIMEO to prevent blocking forever
	 * on sends, when device is too slow or stalls. Just put limit
	 * of one second, or "interval", if it is less.
	 */
	tv.tv_sec = 1;
	tv.tv_usec = 0;
	if (pst_ping_config->interval < 1000) {
		tv.tv_sec = 0;
		tv.tv_usec = 1000 * SCHINT(pst_ping_config->interval);
	}
	setsockopt(pst_ping_config->icmp_sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, sizeof(tv));

	/* Set RCVTIMEO to "interval". Note, it is just an optimization
	 * allowing to avoid redundant poll(). */
	tv.tv_sec = SCHINT(pst_ping_config->interval)/1000;
	tv.tv_usec = 1000*(SCHINT(pst_ping_config->interval)%1000);
	if (setsockopt(pst_ping_config->icmp_sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv))) {
		//options |= F_FLOOD_POLL;
	}
	
	if (!(pst_ping_config->options & F_PINGFILLED)) {
		int i;
		u_char *p = pst_ping_config->outpack+8;
		
		/* Do not forget about case of small datalen,
		 * fill timestamp area too!
		 */
		for (i = 0; i < pst_ping_config->datalen; ++i)
			*p++ = i;
	}

	set_signal(SIGINT, sigexit);
	set_signal(SIGALRM, sigexit);

	sigemptyset(&sset);
	sigprocmask(SIG_SETMASK, &sset, NULL);

	gettimeofday(&pst_ping_config->ping_status.start_time, NULL);
	
	if (pst_ping_config->ping_status.deadline) {
		struct itimerval it;
		it.it_interval.tv_sec = 0;
		it.it_interval.tv_usec = 0;
		it.it_value.tv_sec = pst_ping_config->ping_status.deadline;
		it.it_value.tv_usec = 0;
		setitimer(ITIMER_REAL, &it, NULL);
	}

	if (isatty(STDOUT_FILENO)) {
		struct winsize w;
		if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) {
			if (w.ws_col > 0) {
				screen_width = w.ws_col;
			}
		}
	}
}
static int __check_exit(ping_configuration_packet* pst_ping_conf)
{
	st_ping_status* pstatus =  &pst_ping_conf->ping_status;
	if (exiting) { 
		if (pstatus->isRunning)
			pstatus->exiting = exiting;
		__log_error("pst_ping_conf->exiting = %d\n", exiting);
		//sprintf(printf_buf[send_index-1], "exiting by SIGALRM");
		return 6; 
	}
    if (pst_ping_conf->npackets && 
			pstatus->nreceived + pstatus->nerrors >= pst_ping_conf->npackets) {   
    	__log_debug("pst_ping_conf->npackets is expended\n");
		return 6; 
	}
    if (pstatus->deadline && pstatus->nerrors){   
		if (pstatus->nerrors != 0) {
    		extern int errno;
			//sprintf(printf_buf[send_index-1], "get error %s", strerror(errno));
    	}
		__log_debug("pst_ping_conf->deadline is expended\n");
		return 6; 
	}
	return 0;
}

static int send_probe(ping_configuration_packet* pst_ping_conf)
{
	struct icmphdr *icp;
	int cc;
	int i;

	icp = (struct icmphdr *)pst_ping_conf->outpack;
	icp->type = ICMP_ECHO;
	icp->code = 0;
	icp->checksum = 0;
	icp->un.echo.sequence = htons(pst_ping_conf->ping_status.ntransmitted+1);
	icp->un.echo.id = 0;			/* ID */

	rcvd_clear(pst_ping_conf->ping_status.ntransmitted + 1);

	if (timing) {
		if (pst_ping_conf->options & F_LATENCY) {
			struct timeval tmp_tv;
			gettimeofday(&tmp_tv, NULL);
			memcpy(icp+1, &tmp_tv, sizeof(tmp_tv));
		} else {
			memset(icp+1, 0, sizeof(struct timeval));
		}
	}

	cc = pst_ping_conf->datalen + 8;			/* skips ICMP portion */

	/* compute ICMP checksum here */
	icp->checksum = in_cksum((u_short *)icp, cc, 0);

	if (timing && !(pst_ping_conf->options&F_LATENCY)) {
		struct timeval tmp_tv;
		gettimeofday(&tmp_tv, NULL);
		memcpy(icp+1, &tmp_tv, sizeof(tmp_tv));
		icp->checksum = in_cksum((u_short *)&tmp_tv, sizeof(tmp_tv), ~icp->checksum);
	}

	static struct iovec iov;
	iov.iov_base = (struct icmphdr *)pst_ping_conf->outpack;
	iov.iov_len = 0;
	static struct msghdr m;
	
	m.msg_name = (void*)&pst_ping_conf->destination;
	m.msg_namelen = sizeof(pst_ping_conf->destination);
	m.msg_iov = &iov;
	m.msg_iovlen = 1;
	m.msg_control = &pst_ping_conf->cmsg;
	m.msg_controllen = sizeof(pst_ping_conf->cmsg);
	m.msg_flags = 0;

	m.msg_controllen = sizeof(pst_ping_conf->cmsg);
	iov.iov_len = cc;
	
	do {
		i = sendmsg(pst_ping_conf->icmp_sock, &m, confirm_flag);
		confirm_flag = 0;
	} while (0);

	return (cc == i ? 0 : i);
}



/*
 * pinger -- 
 * 	Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
 * will be added on by the kernel.  The ID field is our UNIX process ID,
 * and the sequence number is an ascending integer.  The first 8 bytes
 * of the data portion are used to hold a UNIX "timeval" struct in VAX
 * byte-order, to compute the round-trip time.
 */
static int pinger(ping_configuration_packet* pst_ping_conf)
{
	st_ping_status* pstatus = &pst_ping_conf->ping_status;
	int i;

	static int oom_count;
	static int tokens;
	
	/* Have we already sent enough? If we have, return an arbitrary positive value. */
	if (pstatus->exiting || 
			(pst_ping_conf->npackets && !pstatus->deadline
				&& pstatus->ntransmitted >= pst_ping_conf->npackets ))
		return 1000;
	
	/* Check that packets < rate * time + preload */
	if (pstatus->cur_time.tv_sec == 0) {
		gettimeofday(&pstatus->cur_time, NULL);
		tokens = pst_ping_conf->interval * (pst_ping_conf->preload-1);
	} else {
		long ntokens;
		struct timeval tv;

		gettimeofday(&tv, NULL);
		ntokens = (tv.tv_sec - pstatus->cur_time.tv_sec)*1000 +
			(tv.tv_usec - pstatus->cur_time.tv_usec)/1000;
		if (!pst_ping_conf->interval) {
			/* Case of unlimited flood is special;
			 * if we see no reply, they are limited to 100pps */
			if (ntokens < MININTERVAL && 
					in_flight(&pst_ping_conf->ping_status) >= pst_ping_conf->preload){
				__log_debug("return  = %d\n", pst_ping_conf->interval - tokens);
				return MININTERVAL-ntokens;
			}	
		}
		ntokens += tokens;
		if (ntokens > pst_ping_conf->interval*pst_ping_conf->preload)
			ntokens = pst_ping_conf->interval*pst_ping_conf->preload;
		if (ntokens < pst_ping_conf->interval)
			return pst_ping_conf->interval - ntokens;

		pstatus->cur_time = tv;
		tokens = ntokens - pst_ping_conf->interval;
	}

resend:

	i = send_probe(pst_ping_conf);
	if (i == 0) {
		oom_count = 0;
		advance_ntransmitted(&pst_ping_conf->ping_status);
		return pst_ping_conf->interval - tokens;
	}

	/* And handle various errors... */
	if (i > 0) {
		/* Apparently, it is some fatal bug. */
		__log_error("unkown error\n");
		abort();
	} else if (errno == ENOBUFS || errno == ENOMEM) {
		int nores_interval;

		/* Device queue overflow or OOM. Packet is not sent. */
		tokens = 0;
		/* Slowdown. This works only in adaptive mode (option -A) 
			FIXME...
		*/
		pst_ping_conf->rtt_addend += (pstatus->rtt < 8*50000 ? pstatus->rtt/8 : 50000);
		if (pst_ping_conf->options & F_ADAPTIVE) {
			update_interval(pst_ping_conf);
		}
			
		nores_interval = SCHINT(pst_ping_conf->interval/2);
		if (nores_interval > 500)
			nores_interval = 500;
		oom_count++;
		if (oom_count*nores_interval < pst_ping_conf->lingertime) {
			__log_debug("return  = %d\n", nores_interval);
			return nores_interval;
		}
			
		i = 0;
		/* Fall to hard error. It is to avoid complete deadlock
		 * on stuck output device even when dealine was not requested.
		 * Expected timings are screwed up in any case, but we will
		 * exit some day. :-) */
	} else if (errno == EAGAIN) {
		/* Socket buffer is full. */
		tokens += pst_ping_conf->interval;
		return MININTERVAL;
	} else {
		if ((i=receive_error_msg(pst_ping_conf)) > 0) {
			/* An ICMP error arrived. */
			__log_debug("An ICMP error arrived\n");
			tokens += pst_ping_conf->interval;
			return MININTERVAL;
		}
		/* Compatibility with old linuces. */
		if (i == 0 && confirm_flag && errno == EINVAL) {
			confirm_flag = 0;
			errno = 0;
		}
		if (!errno)
			goto resend;
	}

	/* Hard local error. Pretend we sent packet. */
	advance_ntransmitted(&pst_ping_conf->ping_status);
	if (i == 0) {
		__log_error("ping: sendmsg");
	}
	tokens = 0;
	return SCHINT(pst_ping_conf->interval);
}


int gather_statistics(__u8 *icmph, int icmplen,
		      int cc, __u16 seq, int hops,
		      int csfailed, struct timeval *tv, char *from,
		      void (*pr_reply)(__u8 *icmph, int cc), ping_configuration_packet* pst_ping_conf,
		      st_handler_packet* phandler)
{
	int dupflag = 0;
	long triptime = 0;
	__u8 *ptr = icmph + icmplen;
	st_ping_status* pstatus = &pst_ping_conf->ping_status;
	__log_debug("have I ?????\n");
	++(pstatus->nreceived);
	if (!csfailed)
		acknowledge(seq, pstatus);

	if (timing && cc >= 8+sizeof(struct timeval)) {
		struct timeval tmp_tv;
		memcpy(&tmp_tv, ptr, sizeof(tmp_tv));
restamp:
		tvsub(tv, &tmp_tv);
		triptime = tv->tv_sec * 1000000 + tv->tv_usec;
		if (triptime < 0) {
			fprintf(stderr, "Warning: time of day goes back (%ldus), taking countermeasures.\n", triptime);
			triptime = 0;
			if (!(pst_ping_conf->options & F_LATENCY)) {
				gettimeofday(tv, NULL);
				pst_ping_conf->options |= F_LATENCY;
				goto restamp;
			}
		}
		if (!csfailed) {
			pstatus->tsum += triptime;
			pstatus->tsum2 += (long long)triptime * (long long)triptime;
			if (triptime < pstatus->tmin)
				pstatus->tmin = triptime;
			if (triptime > pstatus->tmax)
				pstatus->tmax = triptime;
			if (!pstatus->rtt)
				pstatus->rtt = triptime*8;
			else
				pstatus->rtt += triptime - pstatus->rtt/8;
			if (pst_ping_conf->options & F_ADAPTIVE) {
				update_interval(pst_ping_conf);
			}
				
		}
	}
	if (csfailed) {
		pstatus->nchecksum++;
		pstatus->nreceived--;
	} else if (rcvd_test(seq)) {
		pstatus->nrepeats++;
		pstatus->nreceived--;
		dupflag = 1;
	} else {
		rcvd_set(seq);
		dupflag = 0;
	}
	confirm = confirm_flag;

	if (pst_ping_conf->options & F_QUIET) {
		return 1;
	}

	if (pst_ping_conf->options & F_FLOOD) {
		if (!csfailed)
			write_stdout("\b \b", 3);
		else
			write_stdout("\bC", 2);
	} else {
		int i;
		__u8 *cp, *dp;
		print_timestamp(pst_ping_conf->options);
		printf("%d bytes from %s:", cc, from);
		__log_debug("have I ?????\n");
		if (pr_reply)
			pr_reply(icmph, cc);

		if (hops >= 0)
			printf(" ttl=%d", hops);

		if (cc < pst_ping_conf->datalen+8) {
			printf(" (truncated)\n");
			return 1;
		}
		if (timing) {
			if (triptime >= 100000)
				printf(" time=%ld ms", triptime/1000);
			else if (triptime >= 10000)
				printf(" time=%ld.%01ld ms", triptime/1000,
				       (triptime%1000)/100);
			else if (triptime >= 1000)
				printf(" time=%ld.%02ld ms", triptime/1000,
				       (triptime%1000)/10);
			else
				printf(" time=%ld.%03ld ms", triptime/1000,
				       triptime%1000);
		}
		if (dupflag)
			printf(" (DUP!)");
		if (csfailed)
			printf(" (BAD CHECKSUM!)");
		struct icmphdr * tmp_change = (struct icmphdr *)icmph;
		char printf_buf[128];
		memset((void*)printf_buf, '\0', 128);
		sprintf(printf_buf, "%d bytes from %s:  icmp_seq=%u ttl=%d time=%ld.%03ld ms", 
							cc, from, ntohs(tmp_change->un.echo.sequence),
							hops, triptime/1000,  triptime%1000);
		__log_debug("have I ?????\n");
		if (phandler != NULL) {
			__log_debug("printf_buf = %s\n", printf_buf);
			((struct handle_send_msg *)(phandler->arg))->msg.entity = printf_buf;
			__log_debug("entity = %s\n", ((struct handle_send_msg *)(phandler->arg))->msg.entity);
 			if (phandler->handler_func != NULL) {
				__log_debug("have I ?????\n");
				phandler->handler_func(phandler->arg);
				__log_debug("have I ?????\n");
				//phandler->handler_func((void *)printf_buf);
				__log_debug("have I ?????\n");
			}
		}

		/* check the data */
		cp = ((u_char*)ptr) + sizeof(struct timeval);
		dp = &pst_ping_conf->outpack[8 + sizeof(struct timeval)];
		for (i = sizeof(struct timeval); i < pst_ping_conf->datalen; ++i, ++cp, ++dp) {
			if (*cp != *dp) {
				printf("\nwrong data byte #%d should be 0x%x but was 0x%x",
				       i, *dp, *cp);
				cp = (u_char*)ptr + sizeof(struct timeval);
				for (i = sizeof(struct timeval); i < pst_ping_conf->datalen; ++i, ++cp) {
					if ((i % 32) == sizeof(struct timeval))
						printf("\n#%d\t", i);
					printf("%x ", *cp);
				}
				break;
			}
		}
	}
	return 0;
}



static int parse_reply(struct msghdr *msg, int cc, void *addr, struct timeval *tv,
								ping_configuration_packet* pst_ping_conf, st_handler_packet* phandler)
{
	struct sockaddr_in *from = addr;
	__u8 *buf = msg->msg_iov->iov_base;
	struct icmphdr *icp;
	struct iphdr *ip;
	int hlen;
	int csfailed;
	struct cmsghdr *cmsg;
	int ttl;
	__u8 *opts;
	int optlen;
	//char printf_buf[256];

	/* Check the IP header */
	ip = (struct iphdr *)buf;
	
	hlen = 0;
	ttl = 0;
	opts = buf;
	optlen = 0;
	for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
		if (cmsg->cmsg_level != SOL_IP)
			continue;
		if (cmsg->cmsg_type == IP_TTL) {
			if (cmsg->cmsg_len < sizeof(int))
				continue;
			ttl = *(int *) CMSG_DATA(cmsg);
		} else if (cmsg->cmsg_type == IP_RETOPTS) {
			opts = (__u8 *) CMSG_DATA(cmsg);
			optlen = cmsg->cmsg_len;
		}
	}
	

	/* Now the ICMP part  status_snapshot*/
	cc -= hlen;
	icp = (struct icmphdr *)(buf + hlen);
	csfailed = in_cksum((u_short *)icp, cc, 0);

	if (icp->type == ICMP_ECHOREPLY) {
		if (!is_ours(icp->un.echo.id)) {
			__log_debug("icp->un.echo.id = %d\n", icp->un.echo.id);
			return 1;			/* 'Twas not our ECHO */
		}
		if (gather_statistics((__u8*)icp, sizeof(*icp), cc,
				      ntohs(icp->un.echo.sequence),
				      ttl, 0, tv, pr_addr(from->sin_addr.s_addr, pst_ping_conf),
				      pr_echo_reply, pst_ping_conf, phandler))
			return 0;
	} else {
		/* We fall here when a redirect or source quench arrived.
		 * Also this branch processes icmp errors, when IP_RECVERR
		 * is broken. */
		__log_debug();
		switch (icp->type) {
		case ICMP_ECHO:
			/* MUST NOT */
			return 1;
		case ICMP_SOURCE_QUENCH:
		case ICMP_REDIRECT:
		case ICMP_DEST_UNREACH:
		case ICMP_TIME_EXCEEDED:
		case ICMP_PARAMETERPROB:
			{
				struct iphdr * iph = (struct  iphdr *)(&icp[1]);
				struct icmphdr *icp1 = (struct icmphdr*)((unsigned char *)iph + iph->ihl*4);
				int error_pkt;
				if (cc < 8+sizeof(struct iphdr)+8 ||
				    cc < 8+iph->ihl*4+8)
					return 1;
				if (icp1->type != ICMP_ECHO ||
				    iph->daddr != pst_ping_conf->destination.sin_addr.s_addr ||
				    !is_ours(icp1->un.echo.id))
					return 1;
				error_pkt = (icp->type != ICMP_REDIRECT &&
					     icp->type != ICMP_SOURCE_QUENCH);
				if (error_pkt) {
					acknowledge(ntohs(icp1->un.echo.sequence), &pst_ping_conf->ping_status);
					
					static int once;
					/* Sigh, IP_RECVERR for raw socket
					 * was broken until 2.4.9. So, we ignore
					 * the first error and warn on the second.
					 */
					if (once++ == 1)
						fprintf(stderr, "\rWARNING: kernel is not very fresh, upgrade is recommended.\n");
					if (once == 1)
						return 0;
					
				}
				pst_ping_conf->ping_status.nerrors+=error_pkt;
				if (pst_ping_conf->options & F_QUIET)
					return !error_pkt;
				if (pst_ping_conf->options & F_FLOOD) {
					if (error_pkt)
						write_stdout("\bE", 2);
					return !error_pkt;
				}
				print_timestamp(pst_ping_conf->options);
				printf("From %s: icmp_seq=%u ",
				       pr_addr(from->sin_addr.s_addr, pst_ping_conf),
				       ntohs(icp1->un.echo.sequence));
				if (csfailed)
					printf("(BAD CHECKSUM)");
				char hearbuf[512];
				memset((void*)hearbuf, '0', sizeof(hearbuf));
				pr_icmph(icp->type, icp->code, ntohl(icp->un.gateway), icp,
						pst_ping_conf, hearbuf);
				__log_debug("%s", hearbuf);
				return !error_pkt;
			}
		default:
			/* MUST NOT */
			break;
		}
		if ((pst_ping_conf->options & F_FLOOD) && 
				!(pst_ping_conf->options & (F_VERBOSE|F_QUIET))) {
			if (!csfailed)
				write_stdout("!E", 2);
			else
				write_stdout("!EC", 3);
			return 0;
		}
		if (!(pst_ping_conf->options & F_VERBOSE) || pst_ping_conf->uid)
			return 0;
		if (pst_ping_conf->options & F_PTIMEOFDAY) {
			struct timeval recv_time;
			gettimeofday(&recv_time, NULL);
			printf("%lu.%06lu ", (unsigned long)recv_time.tv_sec, (unsigned long)recv_time.tv_usec);
		}
		printf("From %s: ", pr_addr(from->sin_addr.s_addr, pst_ping_conf));
		if (csfailed) {
			printf("(BAD CHECKSUM)\n");
			return 0;
		}
		char hearbuf[512];
		memset((void*)hearbuf, '0', sizeof(hearbuf));
		pr_icmph(icp->type, icp->code, ntohl(icp->un.gateway), icp,
				pst_ping_conf, hearbuf);
		__log_debug("%s", hearbuf);
		
		return 0;
	}

	if (!(pst_ping_conf->options & F_FLOOD)) {
		char tmp[128];
		pr_options(opts, optlen + sizeof(struct iphdr), pst_ping_conf, tmp);

		if (pst_ping_conf->options & F_AUDIBLE)
			putchar('\a');
		putchar('\n');
		fflush(stdout);
	} else {
		putchar('\a');
		fflush(stdout);
	}
	return 0;
}

void reset_status(st_ping_status* pstatus)
{
	memset(pstatus, 0, sizeof(st_ping_status));
	pstatus->isRunning	= 0;
	exiting = 0;
}

								

/*
 * finish --
 *	Print out statistics, and give up.
 */
static void finish(ping_configuration_packet* pst_ping_conf, st_handler_packet* phandler)
{
	st_ping_status* pstatus = &pst_ping_conf->ping_status;
	
	struct timeval tv = pstatus->cur_time;
	char *comma = "";

	tvsub(&tv, &pstatus->start_time);

//	putchar('\n');
//	fflush(stdout);
	char buf[256];
	memset(buf, 0 ,256);
	__log_debug("buf = %s\n", buf);
	sprintf(buf, " ____%s ping statistics ____\n", pst_ping_conf->hostname);
	__log_debug("buf = %s\n", buf);
	sprintf(buf, "%s%ld packets transmitted; ", buf, pstatus->ntransmitted);
	__log_debug("buf = %s\n", buf);
	sprintf(buf, "%s%d received", buf, pstatus->nreceived);
	__log_debug("buf = %s\n", buf);
	if (pstatus->nrepeats)
		sprintf(buf, "%s; +%ld duplicates", buf, pstatus->nrepeats);
	__log_debug("buf = %s\n", buf);
	if (pstatus->nchecksum)
		sprintf(buf, "%s; +%ld corrupted", buf, pstatus->nchecksum);
	__log_debug("buf = %s\n", buf);
	if (pstatus->nerrors)
		sprintf(buf, "%s; +%ld errors", buf, pstatus->nerrors);
	__log_debug("buf = %s\n", buf);
	if (pstatus->ntransmitted) {
		sprintf(buf, "%s; %d%% packet loss", buf,
			   (int) ((((long long)(pstatus->ntransmitted - pstatus->nreceived)) * 100) /
				  pstatus->ntransmitted));
		sprintf(buf, "%s; time %ldms", buf, 1000*tv.tv_sec+tv.tv_usec/1000);
	}
	//putchar('\n');
	__log_debug("buf = %s\n", buf);
	if (pstatus->nreceived && timing) {
		long tmdev;

		pstatus->tsum /= pstatus->nreceived + pstatus->nrepeats;
		pstatus->tsum2 /= pstatus->nreceived + pstatus->nrepeats;
		tmdev = llsqrt(pstatus->tsum2 - pstatus->tsum * pstatus->tsum);

		sprintf(buf, "%srtt min/avg/max/mdev = %ld.%03ld/%lu.%03ld/%ld.%03ld/%ld.%03ld ms", buf,
			   (long)(pstatus->tmin)/1000, (long)(pstatus->tmin)%1000,
			   (unsigned long)((pstatus->tsum)/1000), (long)(pstatus->tsum%1000),
			   (long)(pstatus->tmax)/1000, (long)(pstatus->tmax)%1000,
			   (long)tmdev/1000, (long)tmdev%1000
			   );
		comma = ", ";
		__log_debug("buf = %s\n", buf);
	}
	if (pstatus->pipesize > 1) {
		sprintf(buf, "%s%spipe %d", buf, comma, pstatus->pipesize);
		comma = "; ";
	}
	__log_debug("buf = %s\n", buf);
	if (pstatus->nreceived && 
			(!pst_ping_conf->interval || 
				(pst_ping_conf->options&(F_FLOOD|F_ADAPTIVE))) && pstatus->ntransmitted > 1) {
		int ipg = (1000000*(long long)tv.tv_sec+tv.tv_usec)/(pstatus->ntransmitted-1);
		sprintf(buf, "%s%sipg/ewma %d.%03d/%d.%03d ms", buf,
			   comma, ipg/1000, ipg%1000, pstatus->rtt/8000, (pstatus->rtt/8)%1000);
	}
	//putchar('\n');
	//exit(!nreceived || (deadline && nreceived < npackets));
	__log_debug("buf = %s\n", buf);
	if (phandler != NULL) {
		((struct handle_send_msg *)(phandler->arg))->msg.entity = buf;
 		if (phandler->handler_func != NULL) {
			phandler->handler_func(phandler->arg);
			__log_debug("buf = %s\n", buf);
		}
	}
	__log_debug("buf = %s\n", buf);
	reset_status(&pst_ping_conf->ping_status);
}



void main_loop(ping_configuration_packet* pst_ping_conf, __u8 *packet, 
						int packlen, st_handler_packet* phandler)
{
	char addrbuf[128];
	char ans_data[4096];
	struct iovec iov;
	int next;
	int polling;
	iov.iov_base = (char *)packet;
	int cc;
	struct cmsghdr *c;
	
	if (pst_ping_conf->npackets <= 0) {
        __log_error("ping: bad number of packets to transmit, npackets = %ld\n", pst_ping_conf->npackets);
        return;
    }
	pst_ping_conf->ping_status.isRunning  = 1;
	for (;;) {
		if (__check_exit(pst_ping_conf)) {
			__log_debug("something error\n");
			break;
		}
		do {
            next = pinger(pst_ping_conf);
            next = schedule_exit(next, pst_ping_conf);
        } while (next <= 0);
		polling = 0;
        if ((pst_ping_conf->options & (F_ADAPTIVE|F_FLOOD_POLL)) || 
									next<SCHINT(pst_ping_conf->interval))  {
            int recv_expected = in_flight(&pst_ping_conf->ping_status);
            /* If we are here, recvmsg() is unable to wait for
             * required timeout. */
            if (1000 % HZ == 0 ? next <= 1000 / HZ : (next < INT_MAX / HZ && next * HZ <= 1000)) {
                /* Very short timeout... So, if we wait for
                 * something, we sleep for MININTERVAL.
                 * Otherwise, spin! */
                if (recv_expected) {
                    next = MININTERVAL;
                } else {
                    next = 0;
                    /* When spinning, no reasons to poll.
                     * Use nonblocking recvmsg() instead. */
                    polling = MSG_DONTWAIT;
                    /* But yield yet. */
                    sched_yield();
                }
            }
            if (!polling && (pst_ping_conf->interval)) {
                struct pollfd pset;
                pset.fd = pst_ping_conf->icmp_sock;
                pset.events = POLLIN|POLLERR;
                pset.revents = 0;
                if (poll(&pset, 1, next) < 1 ||
                    !(pset.revents&(POLLIN|POLLERR)))
                    continue;
                polling = MSG_DONTWAIT;
            }
        }	
		for (;;) {
			struct timeval *recv_timep = NULL;
			struct timeval recv_time;
			int not_ours = 0; 
			struct msghdr msg;
			/* Raw socket can receive messages
			* destined to other running pings. */

			iov.iov_len = packlen;
			memset(&msg, 0, sizeof(msg));
			msg.msg_name = addrbuf;
			msg.msg_namelen = sizeof(addrbuf);
			msg.msg_iov = &iov;
			msg.msg_iovlen = 1;
			msg.msg_control = ans_data;
			msg.msg_controllen = sizeof(ans_data);
			cc = recvmsg(pst_ping_conf->icmp_sock, &msg, polling);
			polling = MSG_DONTWAIT;
		
			if (cc < 0) {
				if (errno == EAGAIN || errno == EINTR)
					break;
				if (!receive_error_msg(pst_ping_conf)) {
					if (errno) {
						perror("ping: recvmsg");
						break;
					}
					not_ours = 1;
				}
			} else {	
				for (c = CMSG_FIRSTHDR(&msg); c; c = CMSG_NXTHDR(&msg, c)) {
					if (c->cmsg_level != SOL_SOCKET ||
					    c->cmsg_type != SO_TIMESTAMP)
						continue;
					if (c->cmsg_len < CMSG_LEN(sizeof(struct timeval)))
						continue;
					recv_timep = (struct timeval*)CMSG_DATA(c);
				}
				if ((pst_ping_conf->options&F_LATENCY) || recv_timep == NULL) {
					if ((pst_ping_conf->options&F_LATENCY) ||
					    ioctl(pst_ping_conf->icmp_sock, SIOCGSTAMP, &recv_time)) {
							gettimeofday(&recv_time, NULL);
						}	
					recv_timep = &recv_time;
				}
				not_ours = parse_reply(&msg, cc, addrbuf, recv_timep, pst_ping_conf, phandler);
			}

			/* See? ... someone runs another ping on this host. */
			/*
			if (not_ours && !using_ping_socket) {
				install_filter();
			}
			*/
				

			/* If nothing is in flight, "break" returns us to pinger. */
			if (in_flight(&pst_ping_conf->ping_status) == 0) {
				break;
			}

			/* Otherwise, try to recvmsg() again. recvmsg()
			 * is nonblocking after the first iteration, so that  parse_reply
			 * if nothing is queued, it will receive EAGAIN
			 * and return to pinger.   */
		}	
	}
	//__log_debug();
	//fflush(stdout);
	finish(pst_ping_conf, phandler);
	
}


